#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string>
#include <cstring>

//相关全局变量
namespace shmspace
{
    const char *gPathname = "/home/fwt/Mystudy/gitcode/linux-learning-hub/SharedMemory";
    const int gProjid = 666;
    const int gCreat = 1;
    const int gUser = 0;
    const int gDefault = -1;
    const int gHexBufferSize = 16;
    const int gPerm = 0666;
    const int gShmSize = 4096;
}

//共享内存类
class shm
{
private:
    key_t CreatKey()
    {
        return ftok(_pathname.c_str(), _proj_id);
    }
 
    void CreatShm()
    {
        if (_who == shmspace::gCreat)   //是管理者就创建
        {
            _shmid = shmget(_key, _size, IPC_CREAT | IPC_EXCL | shmspace::gPerm);
            std::cout << " shm sucess creat! key :" << ToHex(_key) << std::endl;
        }
        else if (_who == shmspace::gUser) //不是管理者获取
            _shmid = shmget(_key, _size, IPC_CREAT);
        else
            std::cout << "Invalid identity !" << std::endl;

        if (_shmid == shmspace::gDefault)
            perror("shmget");
    }
    void DeletShm()
    {
        if (_who == shmspace::gCreat)
        {
            shmctl(_shmid, IPC_RMID, nullptr);
            std::cout << " shm sucess delete!" << std::endl;
        }
    }
    void *AttachShm()
    {
        return shmat(_shmid, nullptr, 0);
    }
    void InitShm()
    {
        if (_addr == nullptr)
            return;
        if (_who == shmspace::gCreat)
            memset(_addr, 0, _size);
    }

public:
    shm(const std::string &path, int prog_id, int size, int who)
        : _size(size), _key(CreatKey()), _addr(nullptr),
          _who(who), _shmid(shmspace::gDefault), _proj_id(prog_id)
    {
        CreatShm();   //创建或者获取共享内存
        _addr = AttachShm();  //挂在共享内存
        InitShm();    //初始化共享内存
    }

    void *GetShmAddr()  //获取共享内存起始地址
    {
        return _addr;
    }

    int GetShmNattch()   //查看该共享内存有几个进程挂载
    {
        static shmid_ds shmds;
        shmctl(_shmid, IPC_STAT, &shmds);  //获取信息
        return shmds.shm_nattch;
    }

    const std::string ToHex(key_t key) //将key转为16进制
    {
        char buffer[shmspace::gHexBufferSize];
        snprintf(buffer, shmspace::gHexBufferSize, "0x%x", _key);
        return buffer;
    }

    ~shm()
    {
        shmdt(_addr);   //取消挂载
        DeletShm();     //释放内存空间，删除共享内存
    }

private:

    key_t _key;    //记录唯一标识key，由pathname和proj_id通过ftok生成
    int _size;     //共享内存大小，4096整数倍

    std::string _pathname;
    int _proj_id;

    int _who;      //身份标识
    int _shmid;    //操作系统返回的共享内存id，对共享内存进行操作管理
    void *_addr;   //共享内存挂载后返回的地址，供用户进行访问
};